package cn.lingyangwl.agile.infra.datamanage.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.lingyangwl.agile.common.core.exception.enums.*;
import cn.lingyangwl.agile.infra.*;
import cn.lingyangwl.agile.infra.datamanage.assembly.*;
import cn.lingyangwl.agile.infra.datamanage.mapper.*;
import cn.lingyangwl.agile.infra.datamanage.mode.entity.*;
import cn.lingyangwl.agile.infra.datamanage.mode.rqrs.*;
import cn.lingyangwl.agile.infra.datamanage.service.*;
import cn.lingyangwl.agile.infra.module.datamanage.db.*;
import cn.lingyangwl.agile.infra.module.group.model.rqrs.*;
import cn.lingyangwl.agile.model.enums.*;
import cn.lingyangwl.framework.mybatis.plus.utils.*;
import cn.lingyangwl.framework.tool.core.exception.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yitter.idgen.YitIdHelper;
import com.google.common.base.CaseFormat;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.Validator;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 数据集
 *
 * @author shenguangyang
 */
@Slf4j
@Service
public class DatasetServiceImpl extends ServiceImpl<DatasetMapper, Dataset>
    implements IService<Dataset>, DatasetService {

    @Resource
    private DatasetAssembly datasetAssembly;

    @Resource
    private InfraApi infraApi;

    @Resource
    private Validator validator;

    @Resource
    private DatasourceService datasourceService;
    @Resource
    private DatasetColumnService datasetColumnService;
    
    @Override
    public IPage<DatasetResp> pageDataset(DatasetQueryReq req) {
        LocalDateTime beginTime = req.getBeginLocalDateTime();
        LocalDateTime endTime = req.getEndLocalDateTime();
        LambdaQueryWrapper<Dataset> lqw = Dataset.lqw()
            .eq(Objects.nonNull(req.getGroupId()), Dataset::getGroupId, req.getGroupId())
            .like(StringUtils.isNotEmpty(req.getTableName()), Dataset::getTableName, req.getTableName())
            .ge(Objects.nonNull(beginTime), Dataset::getCreatedAt, beginTime)
            .le(Objects.nonNull(endTime), Dataset::getCreatedAt, endTime)
            .orderByDesc(Dataset::getCreatedAt);
        IPage<Dataset> pageData = this.page(PageUtil.to(req), lqw);
        if (CollUtil.isEmpty(pageData.getRecords())) {
            return new Page<>();
        }
        List<Long> datasourceIds = pageData.getRecords()
            .stream().map(Dataset::getDatasourceId).distinct().collect(Collectors.toList());
        datasourceIds.add(-1L);
        Map<Long, String> datasourceMap = datasourceService.listByIds(datasourceIds).stream()
            .collect(Collectors.toMap(DatasourceConfig::getId, DatasourceConfig::getName));

        List<Long> groupIds = pageData.getRecords().stream().map(Dataset::getGroupId).distinct().collect(Collectors.toList());
        groupIds.add(-1L);
        DataGroupQuery groupReq = new DataGroupQuery();
        groupReq.setIds(groupIds).setBizType(DataGroupBizTypeEnum.DATASET.getType());
        Map<Long, String> groupMap = infraApi.listDataGroup(groupReq).stream()
            .collect(Collectors.toMap(DataGroupDto::getId, DataGroupBase::getName));

        return PageUtil.to(pageData, (e) -> {
            DatasetResp resp = datasetAssembly.toDatasetResp(e);
            resp.setGroupName(groupMap.get(e.getGroupId()));
            resp.setDatasourceName(datasourceMap.get(e.getDatasourceId()));
            return resp;
        });
    }
    
    @Override
    public List<Dataset> listDataset(DatasetQueryReq req) {
        LambdaQueryWrapper<Dataset> lqw = Dataset.lqw()
            .eq(Objects.nonNull(req.getDatasourceId()), Dataset::getDatasourceId, req.getDatasourceId())
            .eq(Objects.nonNull(req.getGroupId()), Dataset::getGroupId, req.getGroupId());
        return this.list(lqw);
    }

    @Override
    public DatasetResp getDataset(Long id) {
        Dataset entity = this.getById(id);
        if (Objects.isNull(entity)) {
            return null;
        }
        DatasetResp resp = datasetAssembly.toDatasetResp(entity);
        if (Objects.nonNull(resp.getDatasourceId())) {
            DatasourceConfig datasourceConfig = datasourceService.getById(resp.getDatasourceId());
            resp.setDatasourceName(Optional.ofNullable(datasourceConfig).map(DatasourceConfig::getName).orElse(""));
        }
        DataGroupDto dataGroup = infraApi.getDataGroup(resp.getGroupId());
        resp.setGroupName(Optional.ofNullable(dataGroup).map(DataGroupDto::getName).orElse(""));
        return resp;
    }

    @Override
    public boolean saveDataset(DatasetSaveReq req) {
        Dataset entity = datasetAssembly.toDataset(req);
    	return this.save(entity);
    }

    @Override
    public boolean updateDataset(DatasetSaveReq req) {
        Dataset entity = datasetAssembly.toDataset(req);
    	return this.updateById(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteDataset(Long id) {
        datasetColumnService.deleteColumn(Collections.singletonList(id), null);
    	return this.removeById(id);
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchDeleteDataset(List<Long> ids) {
        datasetColumnService.deleteColumn(ids, null);
    	return this.removeByIds(ids);
    }

    @Override
    public boolean deleteGroup(DataGroupDeleteReq req) {
        DataGroupQuery listReq = new DataGroupQuery()
            .setIds(req.getIds()).setBizType(req.getBizType());
        List<Long> groupIds = infraApi.listDataGroup(listReq).stream()
            .map(DataGroupDto::getId).collect(Collectors.toList());
        if (CollUtil.isEmpty(groupIds)) {
            return true;
        }
        LambdaQueryWrapper<Dataset> lqw = Dataset.lqw().in(Dataset::getGroupId, groupIds);
        if (this.count(lqw) > 0) {
            throw new BizException(GlobalErrorEnum.DATA_GROUP_USED);
        }

        DataGroupDeleteReq deleteReq = new DataGroupDeleteReq();
        deleteReq.setBizType(DataGroupBizTypeEnum.DATASET.getType()).setIds(req.getIds());
        infraApi.batchDeleteDataGroup(deleteReq);
        return infraApi.batchDeleteDataGroup(deleteReq);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importTable(ImportDatasetTableReq req) {
        List<SQLContext> contextList = this.datasourceService.listSqlContext(req.getDatasourceId(), req.getTableNames());
        DatasetQueryReq queryReq = new DatasetQueryReq();
        queryReq.setDatasourceId(req.getDatasourceId());
        queryReq.setGroupId(req.getGroupId());

        // 查询已有的数据集数据
        List<Dataset> datasetList = this.listDataset(queryReq);
        List<Long> datasetIds = datasetList.stream().map(Dataset::getId).collect(Collectors.toList());
        Map<String, Dataset> datasetMap = datasetList.stream()
            .collect(Collectors.toMap(e -> e.getDatasourceId() + e.getTableName(), Function.identity()));

        // 查询已有的数据集的列数据
        DatasetColumnQueryReq columnReq = new DatasetColumnQueryReq();
        columnReq.setDatasetIds(datasetIds);
        List<DatasetColumn> columnList = datasetColumnService.listColumn(columnReq);
        Map<Long, DatasetColumn> columnMap = columnList.stream().collect(Collectors.toMap(DatasetColumn::getDatasetId, Function.identity()));

        List<DatasetColumn> saveColumnList = new ArrayList<>();
        List<Dataset> saveList = new ArrayList<>();
        for (SQLContext sqlContext : contextList) {
            TableDefinition tableDefinition = sqlContext.getTableDefinition();

            // 初始化数据集数据
            String key = req.getDatasourceId() + tableDefinition.getTableName();
            Dataset dataset = datasetMap.getOrDefault(key, new Dataset(YitIdHelper.nextId()));
            dataset.setTableName(tableDefinition.getTableName())
                .setDisplayName(StringUtils.firstNonEmpty(dataset.getDisplayName(), dataset.getTableName()))
                .setDatasourceId(req.getDatasourceId()).setGroupId(req.getGroupId())
                .setTableComment(tableDefinition.getComment());

            for (ColumnDefinition item : tableDefinition.getColumnDefinitions()) {
                DatasetColumn column = columnMap.getOrDefault(dataset.getId(), new DatasetColumn());
                // 初始化数据集列数据
                String attrName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, item.getColumnName());
                column.setColumnLength(item.getLength()).setColumnScale(item.getScale())
                    .setColumnName(item.getColumnName()).setColumnType(item.getType())
                    .setAttrName(attrName)
                    .setColumnComment(item.getComment()).setIsNullable(item.getIsNullable())
                    .setSortNo(item.getSortNo()).setIsPk(item.getIsPk()).setDatasetId(dataset.getId());
                saveColumnList.add(column);
            }
            saveList.add(dataset);
        }

        this.removeByIds(datasetIds);
        this.saveBatch(saveList);
        datasetColumnService.saveOrUpdateBatch(saveColumnList);
    }
}
